C 语言作为比较古老的一门语言,当下仍有较大的影响力。根据 TIOBE 语言排行榜数据,C 语言处在第二位。

一方面,几乎所有操作系统的大部分代码都是 C 语言编写的;另一方面,很多高级语言的编译器、虚拟机也是 C 语言编写,比如 Java 虚拟机。当然像 Go 语言这种意图抢占 C 语言市场的系统级语言,企图去 C 语言化,自身编译器使用 Go 语言自身实现,通过自举来证明自己,但也无法忽略初代编译器使用 C 语言的事实。

从“高级语言”的角度来看,C 语言处在比汇编高级,而又比 Java 等语言低级的位置。所谓高级语言,是指程序员在编写代码时的容易程度、代码可读性等方面的区别。高级语言更加容易编写、代码可读性更好,但是运行效率相对来说更低。对于代码编写和可读性的优劣,一方面是指语言特性,比如面向对象、模版、泛型等特性是否支持;另一方面代码的可移植性也十分重要。

Java 虚拟机屏蔽了硬件和操作系统细节,开发者只需编写一次代码,便可以在多平台上运行。相对来说,机器码就必须对应固定架构的指令集才能运行。而只是对机器码简单抽象的汇编语言也是如此,x86 架构的汇编程序无法运行在 arm 架构之上。

要谈 C 语言的可移植性,需要先看一下 C 语言的编译过程。

  1. 预处理:对 # 开头的代码进行预处理,如导入头文件内容等;
  2. 编译:将预处理后的代码编译为汇编程序;
  3. 汇编:将汇编代码编译为机器码;
  4. 链接:将上述机器码和其他库文件进行链接,输出可执行程序。

C 语言是通过编译为汇编语言然后编译为机器码执行的。机器码和汇编语言是和硬件架构一一对应,也就是进入汇编步骤之后,就不再有可移植性操作的可能。也就是说 C 语言的可移植性取决于前两步:

  1. 预处理:C 语言中存在一些系统调用等和操作系统强相关的代码,因为不同操作系统的系统调用封装是不同的,所以必须通过条件编译来对不同操作系统进行区分,这种情况需要增加开发者的工作量来兼容不同操作系统,所以可移植性并不在这里体现;
  2. 编译器:同一份 C 语言代码可以通过不同的编译器,导出不同目标平台的汇编代码,当然要对系统调用、系统库等操作系统相关的代码作处理之后。

也就是说,C 语言在一定程度上是可移植的,但是一般来说,稍微复杂的程序就需要我们对系统库的引用、系统调用等代码作出兼容后才能移植到其他平台。相对于 Java 等语言来说逊色不少。虽说 C 语言编译成机器码后执行效率很高,但是 JVM 的 JIT 和 AOT 技术也大大提升 Java 字节码的执行效率,Java 热点代码的执行效率并不逊于 C。

总结

本文简单介绍了下 C 语言的源代码编译为可执行程序的过程和可移植性。然而这个过程中的编译环节涉及到编译原理的知识,链接环节涉及到可执行程序格式、静态动态链接和符号解析,运行环节涉及到操作系统的 VAS、页表和物理内存寻址,进程等内容。每一块都是计算机基础知识中的重头戏,之后有时间我们再来讨论。

Copyright © qingeneral.github.io 2023 all right reserved,powered by Gitbook该文章修订时间: 2023-05-28 13:25:06

results matching ""

    No results matching ""